package com.it00zyq.admin.security;

import com.it00zyq.common.utils.ApiException;
import com.it00zyq.common.enums.ErrorEnum;
import com.it00zyq.common.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author IT00ZYQ
 * @Date 2022/1/8 20:57
 **/
@Component
@RequiredArgsConstructor
public class TokenAuthFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;
    private final MyUserDetailService detailService;

    /**
     * 不进行TOKEN验证的URL
     */
    private final static String ERROR_CONTROLLER = "/api/filter/error";
    private final static String LOGIN = "/api/user/login";
    private final static String REGISTER = "/api/user/register";
    private final static String ARTICLE_LIST = "/api/article/list";
    private final static String ARTICLE_DETAIL = "/api/article/detail";
    private final static String ARTICLE_BASE = "/api/article/base";
    private final static String MAIL_SEND = "/api/mail/send";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        String url = request.getRequestURI();
        System.out.println(url);
        boolean pass = url.equals(ERROR_CONTROLLER) || url.startsWith(LOGIN)
                || url.equals(REGISTER) || url.startsWith(ARTICLE_LIST)
                || url.startsWith(ARTICLE_DETAIL) || url.startsWith(ARTICLE_BASE)
                || url.startsWith(MAIL_SEND);
        // 为不需要TOKEN校验的请求, 直接放行
        if (pass) {
            chain.doFilter(request, response);
            return ;
        }
        // 设置编码
        response.setCharacterEncoding("utf-8");

        // 获取请求头中的token
        String token = request.getHeader(JwtUtil.HEADER);

        if (StringUtils.isBlank(token)) {
            request.setAttribute("exception",
                    new ApiException(ErrorEnum.NO_HAVE_TOKEN));
            request.getRequestDispatcher(ERROR_CONTROLLER)
                    .forward(request, response);
            return;
        }

        Claims claim = null;
        // 进行TOKEN校验
        try{
            claim = jwtUtil.getTokenClaim(token);
        } catch (ExpiredJwtException e) {
            request.setAttribute("exception",
                    new ApiException(ErrorEnum.TOKEN_EXPIRED));
            request.getRequestDispatcher(ERROR_CONTROLLER)
                    .forward(request, response);
            return;
        } catch (Exception e) {
            request.setAttribute("exception",
                    new ApiException(ErrorEnum.INVALID_TOKEN));
            request.getRequestDispatcher(ERROR_CONTROLLER)
                    .forward(request, response);
            return;
        }
        //添加认证用户信息
        UserDetails userDetails = null;
        try {
            userDetails = detailService.loadUserByUsername(claim.getSubject());
        } catch (ApiException e) {
            request.setAttribute("exception", e);
            request.getRequestDispatcher(ERROR_CONTROLLER)
                    .forward(request, response);
            return ;
        }
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

        usernamePasswordAuthenticationToken.setDetails(
                new WebAuthenticationDetailsSource().buildDetails(request));
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        chain.doFilter(request, response);
    }
}
